package com.yfbao.horizon.user.injection.config;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.google.common.collect.Lists;
import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.InlineShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 这种方式时基于配置读取数据库信息的方式，实现用户手动注入dataSource
 * 目前配置中只包含了数据的链接的基本信息，可以扩展配置，然后依赖注入
 */
@Configuration
public class MyShardingDataSourceConfig {

    private static Logger logger = LoggerFactory.getLogger(MyShardingDataSourceConfig.class);

    @Autowired
    private JdbcDb0 db0;
    @Autowired
    private JdbcDb1 db1;
    @Autowired
    private Environment env;

    private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";

    @Bean("dataSource")
    public DataSource getShardingDataSource() throws SQLException {

        Map<String, DataSource> result = new HashMap<>();
        result.put("ds0", ds0());
        result.put("ds1", ds1());

        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration());
        shardingRuleConfig.getTableRuleConfigs().add(getTestTableRuleConfiguration());
//        shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item");
//        shardingRuleConfig.getBroadcastTables().add("t_config");
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "ds${id % 2}"));
//        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("order_id", new ModuloShardingTableAlgorithm()));

        Properties prop = new Properties();
        prop.setProperty("sql.show",Boolean.TRUE.toString());
        return ShardingDataSourceFactory.createDataSource(result, shardingRuleConfig, prop);
    }

    private static KeyGeneratorConfiguration getKeyGeneratorConfiguration() {
        KeyGeneratorConfiguration result = new KeyGeneratorConfiguration("SNOWFLAKE", "id");
        return result;
    }

    TableRuleConfiguration getUserTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("user", "ds${0..1}.user_${0..2}");
        result.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "user_${id % 3}"));
        result.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "ds${id % 2}"));
        result.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        return result;
    }

    TableRuleConfiguration getTestTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("test", "ds${0..1}.test_${0..2}");
        result.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("table_index", "test_${table_index % 3}"));
        result.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("table_index", "ds${table_index % 2}"));
        return result;
    }

    public DataSource ds0() {
        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("type", db0.getType());
        dsMap.put("url", db0.getUrl());
        dsMap.put("username", db0.getUsername());
        dsMap.put("password", db0.getPassword());

        DruidDataSource ds = (DruidDataSource) buildDataSource(dsMap);
        ds.setProxyFilters(Lists.newArrayList(statFilter()));
        // 每个分区最大的连接数
        ds.setMaxActive(20);
        // 每个分区最小的连接数
        ds.setMinIdle(5);
        ds.setValidationQuery("SELECT 1");
        return ds;
    }

    public DataSource ds1() {
        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("type", db1.getType());
        dsMap.put("url", db1.getUrl());
        dsMap.put("username", db1.getUsername());
        dsMap.put("password", db1.getPassword());

        DruidDataSource ds = (DruidDataSource) buildDataSource(dsMap);
        ds.setProxyFilters(Lists.newArrayList(statFilter()));
        // 每个分区最大的连接数
        ds.setMaxActive(20);
        // 每个分区最小的连接数
        ds.setMinIdle(5);
        ds.setValidationQuery("SELECT 1");
        return ds;
    }

    @Bean
    public Filter statFilter() {
        StatFilter filter = new StatFilter();
        filter.setSlowSqlMillis(5000);
        filter.setLogSlowSql(true);
        filter.setMergeSql(true);
        return filter;
    }


    public static DataSource buildDataSource(Map<String, Object> dataSourceMap) {
        Object type = dataSourceMap.get("type");
        if (type == null) {
            type = DATASOURCE_TYPE_DEFAULT;
        }
        try {
            Class<? extends DataSource> dataSourceType;
            dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
            //String driverClassName = dataSourceMap.get("driver").toString();
            String url = dataSourceMap.get("url").toString();
            String username = dataSourceMap.get("username").toString();
            String password = dataSourceMap.get("password").toString();
            // 自定义DataSource配置
            DataSourceBuilder factory = DataSourceBuilder.create().url(url).username(username).password(password).type(dataSourceType);
            return factory.build();
        } catch (Exception e) {
            logger.error("构建数据源" + type + "出错", e);
        }
        return null;
    }

}
